/****************************************************************************
**
** GameWorld.cpp
**
** Copyright (C) August 2014 Hotride
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
*****************************************************************************
*/
//---------------------------------------------------------------------------
#include "stdafx.h"
//---------------------------------------------------------------------------
TGameWorld::TGameWorld(DWORD serial)
: m_PlayerSerial(serial)
{
	m_Items = NULL;
	m_Player = GetWorldObject(serial);
}
//---------------------------------------------------------------------------
TGameWorld::~TGameWorld()
{
	for (WORLD_MAP::iterator i = m_Map.begin(); i != m_Map.end(); i++)
	{
		//delete (*i).second;
		(*i).second = NULL;
	}
	m_Map.clear();
	m_Player = NULL;

	TGameObject *obj = m_Items;
	while (obj != NULL)
	{
		TGameObject *next = obj->m_Next;
		delete obj;
		obj = next;
	}
}
//---------------------------------------------------------------------------
void TGameWorld::SetPlayer(DWORD serial)
{
	if (serial != m_PlayerSerial)
	{
		m_PlayerSerial = serial;
		m_Player = GetWorldObject(serial);
	}
}
//---------------------------------------------------------------------------
TGameObject *TGameWorld::GetWorldObject(DWORD serial)
{
	WORLD_MAP::iterator i = m_Map.find(serial);
	if (i == m_Map.end())
	{
		TGameObject *obj = new TGameObject(serial);
		m_Map[serial] = obj;
		if (m_Items != NULL) m_Items->Add(obj);
		else
		{
			m_Items = obj;
			m_Items->m_Next = NULL;
			m_Items->m_Prev = NULL;
		}
		return obj;
	}
	return (*i).second;
}
//---------------------------------------------------------------------------
TGameObject *TGameWorld::FindWorldObject(DWORD serial)
{
	WORLD_MAP::iterator i = m_Map.find(serial);
	if (i != m_Map.end()) return (*i).second;
	return NULL;
}
//---------------------------------------------------------------------------
void TGameWorld::RemoveObject(TGameObject *obj)
{
	RemoveFromContainer(obj);
	m_Map[obj->GetSerial()] = NULL;
	m_Map.erase(obj->GetSerial());
	delete obj;
}
//---------------------------------------------------------------------------
void TGameWorld::RemoveFromContainer(TGameObject *obj)
{
	if (obj->GetContainer() != 0xFFFFFFFF)
	{
		TGameObject *container = FindWorldObject(obj->GetContainer());
		if (container != NULL) container->Reject(obj);
		else obj->SetContainer(0xFFFFFFFF);
	}
	else
	{
		if (m_Items != NULL)
		{
			if (m_Items->GetSerial() == obj->GetSerial())
			{
				m_Items = m_Items->m_Next;
				if (m_Items != NULL) m_Items->m_Prev = NULL;
			}
			else
			{
				if (obj->m_Next != NULL)
				{
					if (obj->m_Prev != NULL)
					{
						obj->m_Prev->m_Next = obj->m_Next;
						obj->m_Next->m_Prev = obj->m_Prev;
					}
					else //WTF???
					{
						obj->m_Next->m_Prev = NULL;
					}
				}
				else if (obj->m_Prev != NULL)
				{
					obj->m_Prev->m_Next = NULL;
				}
				obj->m_Next = NULL;
				obj->m_Prev = NULL;
			}
		}
	}
}
//---------------------------------------------------------------------------
void TGameWorld::ClearContainer(TGameObject *obj)
{
	if (!obj->Empty()) obj->Clear();
}
//---------------------------------------------------------------------------
void TGameWorld::PutContainer(TGameObject *obj, TGameObject *container)
{
	RemoveFromContainer(obj);
	container->AddItem(obj);
}
//---------------------------------------------------------------------------
void TGameWorld::MoveToTop(TGameObject *obj)
{
	if (obj == NULL || obj->m_Next == NULL) return;

	if (obj->GetContainer() == 0xFFFFFFFF)
	{
		if (obj->m_Prev == NULL)
		{
			m_Items = obj->m_Next;
			m_Items->m_Prev = NULL;

			TGameObject *item = m_Items;

			while (item != NULL)
			{
				if (item->m_Next == NULL)
				{
					item->m_Next = obj;
					obj->m_Prev = item;
					obj->m_Next = NULL;

					return;
				}

				item = item->m_Next;
			}
		}
		else
		{
			TGameObject *item = obj->m_Next;

			obj->m_Prev->m_Next = obj->m_Next;
			obj->m_Next->m_Prev = obj->m_Prev;
			
			while (item != NULL)
			{
				if (item->m_Next == NULL)
				{
					item->m_Next = obj;
					obj->m_Prev = item;
					obj->m_Next = NULL;

					return;
				}

				item = item->m_Next;
			}
		}
	}
	else
	{
		TGameObject *container = FindWorldObject(obj->GetContainer());

		if (container == NULL) return;
		
		if (obj->m_Prev == NULL)
		{
			container->m_Items = obj->m_Next;
			container->m_Items->m_Prev = NULL;

			TGameObject *item = container->m_Items;

			while (item != NULL)
			{
				if (item->m_Next == NULL)
				{
					item->m_Next = obj;
					obj->m_Prev = item;
					obj->m_Next = NULL;

					return;
				}

				item = item->m_Next;
			}
		}
		else
		{
			TGameObject *item = obj->m_Next;

			obj->m_Prev->m_Next = obj->m_Next;
			obj->m_Next->m_Prev = obj->m_Prev;
			
			while (item != NULL)
			{
				if (item->m_Next == NULL)
				{
					item->m_Next = obj;
					obj->m_Prev = item;
					obj->m_Next = NULL;

					return;
				}

				item = item->m_Next;
			}
		}
	}
}
//---------------------------------------------------------------------------
void TGameWorld::ResetNodes()
{
	TGameObject *item = m_Items;
	TGameObject *result = NULL;
	TGameObject *last_item = NULL;

	while (item != NULL)
	{
		item->SetNodeCreated(false);

		item = item->m_Next;
	}
}
//---------------------------------------------------------------------------
TGameObject *TGameWorld::GetXYNode(int X, int Y)
{
	const int MAX_COUNT = 50;

	GAME_OBJECT_NODE node[MAX_COUNT] = {0};

	TGameObject *item = m_Items;
	TGameObject *result = NULL;
	TGameObject *last_item = NULL;

	char MinZ = 127;

	int count = 0;

	while (item != NULL && count < MAX_COUNT)
	{
		if (item->GetGraphic() < 0x4000)
		{
			bool CanAdd = false;

			if (item->GetX() == X && item->GetY() == Y) CanAdd = true;

			/*if (item->GetX() == X && item->GetY() == Y)
			{
				if (item->GetSerial() < 0x40000000)
				{
					if (!item->m_WalkStack.Empty())
					{
						TWalkData *wd = item->m_WalkStack.m_Items;

						BYTE dir = (wd->GetDirection() & 7);

						if (dir < 3 || dir > 5) //not [E SE S]
						{
							CanAdd = true;
						}
					}
					else CanAdd = true;
				}
				else CanAdd = true;
			}
			else if (item->GetSerial() < 0x40000000 && !item->m_WalkStack.Empty())
			{
				TWalkData *wd = item->m_WalkStack.m_Items;

				BYTE dir = (wd->GetDirection() & 7);

				if (dir >= 3 && dir <= 5) //E SE S
				{
					if (item->GetX() + 1 == X && item->GetY() == Y)
					{
						CanAdd = true;
					}
					else if (item->GetX() == X && item->GetY() + 1 == Y)
					{
						CanAdd = true;
					}
					else if (item->GetX() + 1 == X && item->GetY() + 1 == Y)
					{
						CanAdd = true;
					}
				}
			}*/

			if (CanAdd)
			{
				if (item->GetNodeCreated()) return item;

				if (result == NULL)
				{
					result = item;
				}

				item->SetNodeCreated(true);

				item->m_XYNode = NULL;

				if (last_item != NULL)
				{
					last_item->m_XYNode = item;
				}

				last_item = item;

				node[count].Z = item->GetZ();
				node[count].Pointer = item;

				if (item->GetZ() < MinZ) MinZ = item->GetZ();

				count++;
			}
		}

		item = item->m_Next;
	}

	if (count)
	{
		GAME_OBJECT_NODE sorted_node[MAX_COUNT] = {0};

		int sorted_count = 0;

		while (sorted_count < count)
		{
			char NextMinZ = 127;

			IFOR(i, 0, count)
			{
				if (node[i].Pointer != NULL)
				{
					char Z = node[i].Pointer->GetZ();

					if (Z == MinZ)
					{
						sorted_node[sorted_count].Pointer = node[i].Pointer;
						node[i].Pointer = NULL;
						sorted_count++;
					}
					else if (Z < NextMinZ) NextMinZ = Z;
				}
			}

			MinZ = NextMinZ;
		}

		result = sorted_node[0].Pointer;
		result->m_XYNode = NULL;

		IFOR(i, 0, count)
		{
			if (i + 1 < count) sorted_node[i].Pointer->m_XYNode = sorted_node[i + 1].Pointer;
			else sorted_node[i].Pointer->m_XYNode = NULL;
		}

	}

	return result;
}
//---------------------------------------------------------------------------
TGameObject *TGameWorld::GetMultiNode()
{
	TGameObject *item = m_Items;
	TGameObject *result = NULL;
	TGameObject *last_item = NULL;

	while (item != NULL)
	{
		if (item->GetGraphic() >= 0x4000)
		{
			if (item->GetNodeCreated()) return item;

			if (result == NULL)
			{
				result = item;
			}

			item->SetNodeCreated(true);

			item->m_MultiNode = NULL;

			if (last_item != NULL)
			{
				last_item->m_MultiNode = item;
			}

			last_item = item;
		}

		item = item->m_Next;
	}

	return result;
}
//---------------------------------------------------------------------------
void TGameWorld::Dump(BYTE tCount, DWORD serial)
{
	trace_printf("World Dump:\n\n");

	TGameObject *obj = m_Items;

	if (serial != 0xFFFFFFFF)
	{
		obj = FindWorldObject(serial);
		if (obj != NULL) obj = obj->m_Items;
	}

	while (obj != NULL)
	{
		if (obj->GetContainer() == serial)
		{
			if (obj->GetSerial() == m_PlayerSerial) trace_printf("---Player---\n");
			IFOR(i, 0, tCount) trace_printf("\t");

			trace_printf("%s%08X:%04X[%04X](%02X)*%i\tin 0x%08X XYZ=%i,%i,%i on Map %i\n",
				(obj->GetNPC() ? "NPC: " : "Item: "), obj->GetSerial(),
				obj->GetGraphic(), obj->GetColor(), obj->GetLayer(),
				obj->GetCount(), obj->GetContainer(), obj->GetX(),
				obj->GetY(), obj->GetZ(), obj->GetMapIndex());

			if (obj->m_Items != NULL) Dump(tCount + 1, obj->GetContainer());
		}

		obj = obj->m_Next;
	}
}
//---------------------------------------------------------------------------